package com.cold.legendary.bprocess.trigger.impl;

import com.alibaba.fastjson.JSON;
import com.cold.legendary.bprocess.common.constance.BErrorCodeEnum;
import com.cold.legendary.bprocess.common.exception.BProcessException;
import com.cold.legendary.bprocess.common.exception.BProcessInterruptException;
import com.cold.legendary.bprocess.common.utils.ValidateBeanUtils;
import com.cold.legendary.bprocess.context.enums.CallWayEnum;
import com.cold.legendary.bprocess.context.enums.PointTagEnum;
import com.cold.legendary.bprocess.context.ins.BArrowIns;
import com.cold.legendary.bprocess.context.ins.BPointIns;
import com.cold.legendary.bprocess.context.manager.BProcessManager;
import com.cold.legendary.bprocess.context.run.BContext;
import com.cold.legendary.bprocess.db.model.TbProcessRequest;
import com.cold.legendary.bprocess.definition.point.BPointService;
import com.cold.legendary.bprocess.trigger.AbstractBProcessTriggerService;
import com.cold.legendary.bprocess.trigger.enums.TriggerModeEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

/**
 * @author cold
 * @since 2023/3/10
 */
@Slf4j
@Service
public class ExecuteBProcessTriggerService extends AbstractBProcessTriggerService {

    @Override
    public void triggerBPoint(BPointIns triggerBPointIns, BContext bContext) {
        try {
            //校验参数合法性
            ValidateBeanUtils.validateAnnotation(bContext);

            String bizId = bContext.getBizId();
            String merchantId = bContext.getMerchantId();
            String processId = bContext.getProcessId();
            String requestId = bContext.getRequestId();

            //如果不是起始节点
            if (!(PointTagEnum.START == triggerBPointIns.getPointTagEnum())) {
                TbProcessRequest qryProcessRequest = processRequestRepository.queryTbProcessRequest(requestId);
                if (null == qryProcessRequest) {
                    log.info("运行过程,流程请求记录不存在!!!bizId={}, merchantId={},processId={}", bizId, merchantId, processId);
                    throw new BProcessException(BErrorCodeEnum.SYS_RUN_PROCESS_REQUEST_NONE_ERROR);
                }

                //获取流程定义中,获取当前应该属于的节点与流程表判断,是否允许流转
                //注意,如果在发布过程中的非常极端情况,可能程序运行到此处,去获取流程定义发生了变更,
                // 可能导致流程定义的数值与请求表可能存在短暂不一致,这种情况下,自动重试即可解决
                //为了尽量避免此类问题发生,请注意编码过程,尽量不要改动已发布的流程编排,
                // 如果要变更,请重新定义process,用新的processid调用,则无此问题;

                //大多数情况的此处报错,是因为数据异常了,需要排查
                BPointIns curBPointInsOnProcess = BProcessManager.getPreviousCurBPointInsOnProcess(processId, triggerBPointIns.getPointId());
                if (null == curBPointInsOnProcess || !curBPointInsOnProcess.getPointId().equals(qryProcessRequest.getPointId())) {
                    log.error("节点不匹配,不允许流转,当前节点={}, 将要流转的节点={}, 定义中当前应该是的节点={}",
                            qryProcessRequest.getPointId(), triggerBPointIns.getPointId(), (null == curBPointInsOnProcess) ? null : curBPointInsOnProcess.getPointId());
                    throw new BProcessException(BErrorCodeEnum.SYS_POINT_NOT_MATCH_ERROR);
                }
                //节点发生了变更
                bContext.setPointId(triggerBPointIns.getPointId());
                bProcessTransactionService.roamPoint(bContext, curBPointInsOnProcess.getPointId(), triggerBPointIns);
            }

            //异步节点
            if (CallWayEnum.WAIT == triggerBPointIns.getCallWayEnum()) {
                log.info("异步节点触发等待!triggerBPointId={}, bContext={}", triggerBPointIns.getPointId(), JSON.toJSONString(bContext));
                //修改节点状态为等待中
                bProcessTransactionService.waitPoint(bContext, triggerBPointIns.getPointId());
                return;
            }

            //执行当前节点不同场景的业务逻辑
            log.info("<execute>-开始...triggerBPointId={}, bContext={}", triggerBPointIns.getPointId(), JSON.toJSONString(bContext));
            BPointService bPointService = triggerBPointIns.getBPointService();
            bPointService.execute(bContext);
            log.info("<execute>-结束...triggerBPointId={}, bContext={}", triggerBPointIns.getPointId(), JSON.toJSONString(bContext));

            if (PointTagEnum.END == triggerBPointIns.getPointTagEnum()) {
                //如果节点是结束节点,更新为已结束
                bProcessTransactionService.finishPoint(bContext, triggerBPointIns.getPointId());
                return;
            }

            //判断当前节点是否是同步节点
            BArrowIns nextBArrowIns = triggerBPointIns.getNextBArrowIns();
            BPointIns nextBPointIns = nextBArrowIns.getNextBPointIns();
            //同步类型的节点递归调用
            triggerBPoint(nextBPointIns, bContext);
        } catch (BProcessInterruptException e) {
            log.info("捕捉到节点中断信号!执行中断逻辑! code={}, msg={}", e.getCode(), e.getMsg());
            if (StringUtils.isEmpty(bContext.getFailReason())) {
                bContext.setFailReason(e.getMsg());
            }
            bProcessService.interruptBProcess(bContext);
        } catch (Exception e) {
            log.error("触发节点逻辑发生异常!", e);
            if (null == bContext || StringUtils.isAnyEmpty(bContext.getBizId(), bContext.getMerchantId(), bContext.getProcessId())) {
                log.error("缺失必要参数,无法记录重试数据,无法进行节点自动重试!");
                throw new BProcessException(BErrorCodeEnum.SYS_RETRY_MISS_PARAMS_ERROR, e);
            }
            bProcessTransactionService.errorPoint(bContext, e);
        }
    }

    @Override
    protected TriggerModeEnum getTriggerModeEnum() {
        return TriggerModeEnum.EXECUTE;
    }
}
